package apuntes;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import aima.core.agent.Action;
import aima.core.agent.impl.DynamicAction;
import aima.core.search.framework.GraphSearch;
import aima.core.search.framework.Problem;
import aima.core.search.framework.Search;
import aima.core.search.framework.SearchAgent;
import aima.core.search.framework.TreeSearch;
import aima.core.search.informed.AStarSearch;
import aima.core.search.informed.GreedyBestFirstSearch;
import aima.core.search.local.SimulatedAnnealingSearch;
import aima.core.search.uninformed.BreadthFirstSearch;
import aima.core.search.uninformed.IterativeDeepeningSearch;

public class EstadoCoches {

	public static final int n = 6;
	/********************/
	private int solX = 4;
	private int solY = 2;
	/********************/
	private boolean tablero[][];
	private ArrayList<Coche> listaCoches;
	private Coche cocheAMover;
	
	public static Action AtrasR = new DynamicAction("AtrasR");
	public static Action DelanteR = new DynamicAction("DelanteR");

	public static Action AtrasA = new DynamicAction("AtrasA");
	public static Action DelanteA = new DynamicAction("DelanteA");
	
	public static Action AtrasV = new DynamicAction("AtrasV");
	public static Action DelanteV = new DynamicAction("DelanteV");
	
	public static Action AtrasS = new DynamicAction("AtrasS");
	public static Action DelanteS = new DynamicAction("DelanteS");
	
	public static Action AtrasZ = new DynamicAction("AtrasZ");
	public static Action DelanteZ = new DynamicAction("DelanteZ");
	
	public static Action AtrasG = new DynamicAction("AtrasG");
	public static Action DelanteG = new DynamicAction("DelanteG");
	
	public static Action AtrasC2 = new DynamicAction("AtrasC2");
	public static Action DelanteC2 = new DynamicAction("DelanteC2");
	
	public static Action AtrasC1 = new DynamicAction("AtrasC1");
	public static Action DelanteC1 = new DynamicAction("DelanteC1");
	
	
		
		public EstadoCoches() {
			
			tablero = new boolean[6][6];
			for (int i = 0; i <6; i++)
				for (int j = 0; j <6; j++)
					tablero[i][j] = false;
			
			cochesComoElEjemplo();
			//aFaltaDe1Mov();
	}
		
		public EstadoCoches(Coche c){
			cocheAMover = c;
		}
		
		public EstadoCoches(EstadoCoches c){
			copiaTablero(c.getTablero());
			copiaListaCoches(c.getListaCoches());
		}
		
		private void copiaListaCoches(ArrayList<Coche> lc){
			
			listaCoches = new ArrayList<Coche>();
			Coche c;
			for (int i = 0; i < lc.size(); i++){
				 c = new Coche(lc.get(i));
				listaCoches.add(c);
			}			
			cocheAMover = listaCoches.get(0);
		}
		
		
		private void copiaTablero(boolean[][] t) {
			tablero = new boolean[6][6];
			for (int j = 0; j<n; j++)
				for (int i = 0; i<n; i++){
					tablero[i][j] = t[i][j];
				}
		}
				
		public boolean[][] getTablero(){
			return tablero;
		}

		private void marcaCoche(Coche c){
			
			int x = c.getX();
			int y = c.getY();
			if (c.isHorizontal()){
				for(int i = 0; i < c.getLongitud(); i++){
					tablero[x+i][y] = true;
				}
			}
			else{
				for(int i = 0; i < c.getLongitud(); i++){
					tablero[x][y+i] = true;
				}				
			}
		}
		
	private void cochesComoElEjemplo(){
		/*Orientacion, X1, Y1, X2, Y2

		R: H,2,2,3,2
		A: H,2,1,3,1
		Z: H,4,4,5,4
		V: V,4,0,4,1		
		S: V,4,2,4,3
		G: V,3,4,3,5
		C1:V,5,0,5,2
		C2:V,2,3,2,5*/
		
		listaCoches = new ArrayList<Coche>();
		
		Coche r = new Coche("R",2,2,2,true);
		marcaCoche(r);
		listaCoches.add(r);
		
		cocheAMover = r;
 
 		Coche a = new Coche("A",2,1,2,true);
		listaCoches.add(a);
		marcaCoche(a);

		Coche z = new Coche("Z",4,4,2,true);
		listaCoches.add(z);
		marcaCoche(z);		
		
		Coche v = new Coche("V",4,0,2,false);
		listaCoches.add(v);
		marcaCoche(v);
		
		Coche s = new Coche("S",4,2,2,false);
		listaCoches.add(s);
		marcaCoche(s);
		
		Coche g = new Coche("G",3,4,2,false);
		listaCoches.add(g);
		marcaCoche(g);	
		
		Coche c1 = new Coche("C1",5,0,3,false);
		listaCoches.add(c1);
		marcaCoche(c1);
		
		Coche c2 = new Coche("C2",2,3,3,false);
		listaCoches.add(c2);
		marcaCoche(c2);

	}

	@SuppressWarnings("unused")
	private void aFaltaDe1Mov(){
      /*Estado casi final
		Orientacion, X1, Y1, X2, Y2

		R: H, 3,2,5,2 //a falta de mover delante una vez
		A: H,0,1,1,1
		Z: H,0,4,1,4
		V: V,4,0,4,1
		S: V,4,4,4,5
		G: V,3,0,3,1
		C1:V,5,3,5,5
		C2:V,2,3,2,5
	  */
		
		listaCoches = new ArrayList<Coche>();
	
		Coche r = new Coche("R",3,2,2,true);
		marcaCoche(r);
		listaCoches.add(r);

		cocheAMover = r;
		
		Coche a = new Coche("A",0,1,2,true);
		listaCoches.add(a);
		marcaCoche(a);

		Coche z = new Coche("Z",0,4,2,true);
		listaCoches.add(z);
		marcaCoche(z);		
		
		Coche v = new Coche("V",4,0,2,false);
		listaCoches.add(v);
		marcaCoche(v);
		
		Coche s = new Coche("S",4,4,2,false);
		listaCoches.add(s);
		marcaCoche(s);
	
		Coche g = new Coche("G",3,0,2,false);
		listaCoches.add(g);
		marcaCoche(g);
	
		Coche c1 = new Coche("C1",5,3,3,false);
		listaCoches.add(c1);
		marcaCoche(c1);
		
		Coche c2 = new Coche("C2",2,3,3,false);
		listaCoches.add(c2);
		marcaCoche(c2);
	}

	/********************************************************/

	public void moveAtras(Coche c) { //abajo
		
		cocheAMover = c;
		int x = cocheAMover.getX();
		int y = cocheAMover.getY();
		if (cocheAMover.isHorizontal()){
			tablero[x+cocheAMover.getLongitud()-1][y] = false; //la posicion que se va a quedar "vacia", imaginar un coche en horizontal
			cocheAMover.setX(x-1);
			marcaCoche(cocheAMover);
		}
		else{
			tablero[x][y] = false; //la posicion que se va a quedar "vacia", imaginar un coche en horizontal
			cocheAMover.setY(y+1);
			marcaCoche(cocheAMover);
		}

	}
	
	
	public void moveDelante(Coche c) { //arriba
	
		cocheAMover = c;
		int x = cocheAMover.getX();
		int y = cocheAMover.getY();
		
		if (cocheAMover.isHorizontal()){
			tablero[x][y] = false;
			cocheAMover.setX(x+1);
			marcaCoche(cocheAMover);
		}
		else{
			tablero[x][y+cocheAMover.getLongitud()-1] = false;
			cocheAMover.setY(y-1);
			marcaCoche(cocheAMover);
		}
	}
	
	private void seleccionaCochePorAccion(Action where){
		
		if (where.equals(AtrasR) || where.equals(DelanteR) ){
			cocheAMover = listaCoches.get(0);
		}
		
		else if (where.equals(AtrasA) || where.equals(DelanteA) ){
			cocheAMover = listaCoches.get(1);
		}
		
		else if (where.equals(AtrasZ) || where.equals(DelanteZ) ){
			cocheAMover = listaCoches.get(2);
		}
		
		else if (where.equals(AtrasV) || where.equals(DelanteV) ){
			cocheAMover = listaCoches.get(3);
		}
		
		else if (where.equals(AtrasS) || where.equals(DelanteS) ){
			cocheAMover = listaCoches.get(4);
		}
		
		else if (where.equals(AtrasG) || where.equals(DelanteG) ){
			cocheAMover = listaCoches.get(5);
		}
		
		else if (where.equals(AtrasC1) || where.equals(DelanteC1) ){
			cocheAMover = listaCoches.get(6);
		}
		
		else if (where.equals(AtrasC2) || where.equals(DelanteC2) ){
			cocheAMover = listaCoches.get(7);
		}
	
	}
	
	public boolean movimientoValido(Action where) {
		
		
		seleccionaCochePorAccion(where);
		
		boolean valido = false;
		int longitud = cocheAMover.getLongitud();
		int x = cocheAMover.getX();
		int y = cocheAMover.getY();
		
		if (where.equals(AtrasR) ||where.equals(AtrasA) ||where.equals(AtrasZ) ||where.equals(AtrasV) ||where.equals(AtrasS) ||where.equals(AtrasG) ||where.equals(AtrasC1)
				|| where.equals(AtrasC2)) { //abajo

			if (cocheAMover.isHorizontal()){
				
				if (x != 0 && !tablero[x-1][y]){

					return true;
				}
				
			}
			else{
				boolean a = y + longitud != n && !tablero[x][y+longitud];
				boolean b = longitud == 3;
				boolean c = longitud == 2;
				boolean caso1 = a && b;
				boolean caso2 = a && c;
				if (caso1){

					valido = !peligro();
				}
				if (caso2){

					valido = !peligro();
				}
			}
		}
		else if (where.equals(DelanteR) ||where.equals(DelanteA) ||where.equals(DelanteZ) ||where.equals(DelanteV) ||where.equals(DelanteS) ||where.equals(DelanteG) ||
				where.equals(DelanteC1) || where.equals(DelanteC2)) {
			if (cocheAMover.isHorizontal()){
				
				boolean a = x+longitud != n && !tablero[x + longitud][y];
				boolean b = longitud == 3;
				boolean c = longitud == 2;
				boolean caso1 =(a && b);
				boolean caso2 =(a && c);
				if (caso1){

					valido = !peligro();
				}
				if (caso2){

					valido = !peligro();
				}
				
			}
			else{
				if (cocheAMover.getY() != 0 && !tablero[cocheAMover.getX()][cocheAMover.getY()-1]){
					valido = !peligro();
				}				
			}

		}
		
		
		return valido;
	}
	
	private boolean peligro() {
		return false;
	}
	
	public String toString(){
		pintaTablero();
		String s = "CocheAMover: " + cocheAMover + "\n";				
		return s;
	}
	
	public Coche getCoche(){
		return cocheAMover;
	}
	
	public boolean equals(Object o) {
		if (this == o) {
			return true;
		}

		if ((o == null) || (this.getClass() != o.getClass())) {
			return false;
		}
		
		EstadoCoches estado = (EstadoCoches) o;
		return compruebaPosiciones(estado);
	}
	
		private boolean compruebaPosiciones(EstadoCoches estado){
			
			if (estado.getListaCoches() == null) {
				Coche c = estado.getCoche();
				Coche cocheRojo = this.getListaCoches().get(0);
				return ( (c.getX() == cocheRojo.getX()) && (c.getY() == cocheRojo.getY()) );
			}
			for(int i = 0; i <listaCoches.size(); i++){
				Coche c = estado.getListaCoches().get(i);
				Coche thisc = listaCoches.get(i);
					if (! (c.getX() == thisc.getX()) && (c.getY() == thisc.getY()) ) return false;
			}
			return true;
		}
		
		public boolean isBusy(int x, int y){
			return tablero[x][y];
		}
		
		public ArrayList<Coche> getListaCoches(){
			return listaCoches;
		}
	
		@SuppressWarnings("unused")
		private static void eightPuzzleDLSDemo(EstadoCoches estado) {
			System.out.println("\nRetoAtasco recursive DLS (9) -->");
			try {
				Problem problem = new Problem(estado, CochesFunctionFactory
						.getActionsFunction(), CochesFunctionFactory
						.getResultFunction(), new MisionerosGoalTest());
				Search search = new BreadthFirstSearch(new GraphSearch());
			//	Search search = new DepthLimitedSearch(9);
				SearchAgent agent = new SearchAgent(problem, search);				
				printActions(agent.getActions());
				printInstrumentation(agent.getInstrumentation());
			} catch (Exception e) {
				e.printStackTrace();
			}

		}

		@SuppressWarnings("unused")
		private static void eightPuzzleIDLSDemo(EstadoCoches estado) {
			System.out.println("\nMisioneros Iterative DLS -->");
			try {
				Problem problem = new Problem(estado, CochesFunctionFactory
						.getActionsFunction(), CochesFunctionFactory
						.getResultFunction(), new MisionerosGoalTest());
				Search search = new IterativeDeepeningSearch();
				SearchAgent agent = new SearchAgent(problem, search);
				printActions(agent.getActions());
				printInstrumentation(agent.getInstrumentation());
			} catch (Exception e) {
				e.printStackTrace();
			}

		}
		

		/**con heuristicas**/
		
		
		
		@SuppressWarnings("unused")
		private static void eightPuzzleSimulatedAnnealingDemo(EstadoCoches estado) {
			System.out.println("\nMisioneros Simulated Annealing  Search -->");
			try {
				Problem problem = new Problem(estado, CochesFunctionFactory
						.getActionsFunction(), CochesFunctionFactory
						.getResultFunction(), new MisionerosGoalTest());
				SimulatedAnnealingSearch search = new SimulatedAnnealingSearch(
						new Heuristica());
				SearchAgent agent = new SearchAgent(problem, search);
				printActions(agent.getActions());
				System.out.println("Search Outcome=" + search.getOutcome());
				System.out.println("Final State=\n" + search.getLastSearchState());
				printInstrumentation(agent.getInstrumentation());
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		
		@SuppressWarnings("unused")
		private static void eightPuzzleGreedyBestFirstDemo(EstadoCoches estado) {
			System.out
					.println("\nMisioneros Greedy Best First Search (MisplacedTileHeursitic) Graph Search-->");
			try {
				Problem problem = new Problem(estado,
						CochesFunctionFactory.getActionsFunction(),
						CochesFunctionFactory.getResultFunction(),
						new MisionerosGoalTest());
				Search search = new GreedyBestFirstSearch(new GraphSearch(),
						new Heuristica());
				SearchAgent agent = new SearchAgent(problem, search);
				printActions(agent.getActions());
				printInstrumentation(agent.getInstrumentation());
			} catch (Exception e) {
				e.printStackTrace();
			}

		}
		
		@SuppressWarnings("unused")
		private static void eightPuzzleGreedyBestFirstDemoTree(EstadoCoches estado) {
			System.out
					.println("\nMisioneros Greedy Best First Search (MisplacedTileHeursitic) Tree Search-->");
			try {
				Problem problem = new Problem(estado,
						CochesFunctionFactory.getActionsFunction(),
						CochesFunctionFactory.getResultFunction(),
						new MisionerosGoalTest());
				Search search = new GreedyBestFirstSearch(new TreeSearch(),
						new Heuristica());
				SearchAgent agent = new SearchAgent(problem, search);
				printActions(agent.getActions());
				printInstrumentation(agent.getInstrumentation());
			} catch (Exception e) {
				e.printStackTrace();
			}

		}
		
		@SuppressWarnings("unused")
		private static void eightPuzzleGreedyBestFirstManhattanDemo(EstadoCoches estado) {
			System.out
					.println("\nMisioneros Greedy Best First Search (ManhattanHeursitic) Graph Search-->");
			try {
				Problem problem = new Problem(estado,
						CochesFunctionFactory.getActionsFunction(),
						CochesFunctionFactory.getResultFunction(),
						new MisionerosGoalTest());
				Search search = new GreedyBestFirstSearch(new GraphSearch(),
						new Heuristica());
				SearchAgent agent = new SearchAgent(problem, search);
				printActions(agent.getActions());
				printInstrumentation(agent.getInstrumentation());
			} catch (Exception e) {
				e.printStackTrace();
			}

		}


		@SuppressWarnings("unused")
		private static void eightPuzzleGreedyBestFirstManhattanDemoTree(EstadoCoches estado) {
			System.out
					.println("\nMisioneros Greedy Best First Search (ManhattanHeursitic) Tree Search-->");
			try {
				Problem problem = new Problem(estado,
						CochesFunctionFactory.getActionsFunction(),
						CochesFunctionFactory.getResultFunction(),
						new MisionerosGoalTest());
				Search search = new GreedyBestFirstSearch(new TreeSearch(),
						new Heuristica());
				SearchAgent agent = new SearchAgent(problem, search);
				printActions(agent.getActions());
				printInstrumentation(agent.getInstrumentation());
			} catch (Exception e) {
				e.printStackTrace();
			}

		}
		
		private static void eightPuzzleAStarDemo(EstadoCoches estado) {
			System.out
					.println("\nCoches AStar Search (MisplacedTileHeursitic) Graph Search-->");
			try {
				Problem problem = new Problem(estado, CochesFunctionFactory
						.getActionsFunction(), CochesFunctionFactory
						.getResultFunction(), new MisionerosGoalTest());
				Search search = new AStarSearch(new GraphSearch(),
						new Heuristica());
				SearchAgent agent = new SearchAgent(problem, search);
				printActions(agent.getActions());
				printInstrumentation(agent.getInstrumentation());
			} catch (Exception e) {
				e.printStackTrace();
			}

		}
		
		@SuppressWarnings("unused")
		private static void eightPuzzleAStarDemoTree(EstadoCoches estado) {
			System.out
					.println("\nMisioneros AStar Search (MisplacedTileHeursitic) Tree Search-->");
			try {
				Problem problem = new Problem(estado, CochesFunctionFactory
						.getActionsFunction(), CochesFunctionFactory
						.getResultFunction(), new MisionerosGoalTest());
				Search search = new AStarSearch(new TreeSearch(),
						new Heuristica());
				SearchAgent agent = new SearchAgent(problem, search);
				printActions(agent.getActions());
				printInstrumentation(agent.getInstrumentation());
			} catch (Exception e) {
				e.printStackTrace();
			}

		}
		
		@SuppressWarnings("unused")
		private static void eightPuzzleAStarManhattanDemoGraph(EstadoCoches estado) {
			System.out
					.println("\nEstadoMisioneros AStar Search (ManhattanHeursitic) Graph Search-->");
			try {
				Problem problem = new Problem(estado, CochesFunctionFactory
						.getActionsFunction(), CochesFunctionFactory
						.getResultFunction(), new MisionerosGoalTest());
				Search search = new AStarSearch(new GraphSearch(),
						new Heuristica());
				SearchAgent agent = new SearchAgent(problem, search);
				printActions(agent.getActions());
				printInstrumentation(agent.getInstrumentation());
			} catch (Exception e) {
				e.printStackTrace();
			}

		}
		
		@SuppressWarnings("unused")
		private static void eightPuzzleAStarManhattanDemoTree(EstadoCoches estado) {
			System.out
					.println("\nEstadoMisioneros AStar Search (ManhattanHeursitic) Tree Search-->");
			try {
				Problem problem = new Problem(estado, CochesFunctionFactory
						.getActionsFunction(), CochesFunctionFactory
						.getResultFunction(), new MisionerosGoalTest());
				Search search = new AStarSearch(new TreeSearch(),
						new Heuristica());
				SearchAgent agent = new SearchAgent(problem, search);
				printActions(agent.getActions());
				printInstrumentation(agent.getInstrumentation());
			} catch (Exception e) {
				e.printStackTrace();
			}

		}
		
		private static void printInstrumentation(Properties properties) {
			Iterator<Object> keys = properties.keySet().iterator();
			while (keys.hasNext()) {
				String key = (String) keys.next();
				String property = properties.getProperty(key);
				System.out.println(key + " : " + property);
			}

		}

		private static void printActions(List<Action> actions) {
			for (int i = 0; i < actions.size(); i++) {				
				String action = actions.get(i).toString();
				System.out.println(action);
			}
		}
		
		public int getSolX() {
			return solX;
		}
		
		public int getSolY() {
			return solY;
		}
		
		public static void main(String[] args){
			EstadoCoches estado = new EstadoCoches();
			estado.pintaTablero();
//			eightPuzzleDLSDemo(estado);
			eightPuzzleAStarDemo(estado);			
		}	
		
		public void pintaTablero(){
			if (tablero == null)
				System.out.println("***NO HAY TABLERO***");
			else{
				for (int j = 0; j < n; j++)//x
					for (int i = 0; i < n; i++){ //y
						System.out.print("|");
						if (tablero[i][j])
							System.out.print("X");
						else
							System.out.print(" ");
						
						if (i == n-1)
							System.out.print("|\n");
				}
				
			}
			System.out.print("\n\n\n");
	}
		
}
